#include "DAP_config.h"
#include "DAP.h"
#include "MULT.h"
#include "MULT_I2C.h"

static bool i2c_send_ack = 1;		 // 接收是否发送ACK
static uint8_t i2c_error_status = 0; // 总线错误状态

#define SCL_DIR_OUT()                               \
	do                                              \
	{                                               \
		GPIO->SET[PIN_PIO_PORT] = PIN_TCK_DIR_MASK; \
		GPIO->DIRSET[PIN_PIO_PORT] = PIN_TCK_MASK;  \
	} while (0)

#define SCL_DIR_IN()                                \
	do                                              \
	{                                               \
		GPIO->CLR[PIN_PIO_PORT] = PIN_TCK_DIR_MASK; \
		GPIO->DIRCLR[PIN_PIO_PORT] = PIN_TCK_MASK;  \
	} while (0)

#define SDA_DIR_OUT()                               \
	do                                              \
	{                                               \
		GPIO->SET[PIN_PIO_PORT] = PIN_TMS_DIR_MASK; \
		GPIO->DIRSET[PIN_PIO_PORT] = PIN_TMS_MASK;  \
	} while (0)

#define SDA_DIR_IN()                                \
	do                                              \
	{                                               \
		GPIO->CLR[PIN_PIO_PORT] = PIN_TMS_DIR_MASK; \
		GPIO->DIRCLR[PIN_PIO_PORT] = PIN_TMS_MASK;  \
	} while (0)

#define SCL_OUT(x)                              \
	do                                          \
	{                                           \
		if (x)                                  \
		{                                       \
			SCL_DIR_IN();                       \
		}                                       \
		else                                    \
		{                                       \
			GPIO->B[PIN_PIO_PORT][PIN_TCK] = x; \
			SCL_DIR_OUT();                      \
		}                                       \
	} while (0)

#define SDA_OUT(x)                              \
	do                                          \
	{                                           \
		if (x)                                  \
		{                                       \
			SDA_DIR_IN();                       \
		}                                       \
		else                                    \
		{                                       \
			GPIO->B[PIN_PIO_PORT][PIN_TMS] = x; \
			SDA_DIR_OUT();                      \
		}                                       \
	} while (0)

#define SCL_IN() GPIO->B[PIN_PIO_PORT][PIN_TCK]

#define SDA_IN() GPIO->B[PIN_PIO_PORT][PIN_TMS]

#define I2C_DELAY() PIN_DELAY_SLOW(DAP_Data.clock_delay)

/**
 * @brief 发送开始位
 *
 */
static void i2c_start(void)
{
	i2c_send_ack = 1;
	i2c_error_status = 0U; // 清除错误标志

	if (!SDA_IN())
	{
		i2c_error_status |= I2C_STATUS_SDA_GRASP;
	}

	if (!SCL_IN())
	{
		i2c_error_status |= I2C_STATUS_SCL_GRASP;
	}

	SDA_OUT(0); // SDA下降沿
	I2C_DELAY();
	SCL_OUT(0);
	I2C_DELAY();
}

/**
 * @brief 发送结束位
 *
 */
static void i2c_stop(void)
{
	SCL_OUT(0);
	I2C_DELAY();
	SDA_OUT(0);
	I2C_DELAY();
	SCL_OUT(1);
	I2C_DELAY();
	SDA_OUT(1); // SDA上升沿
}

/**
 * @brief 写一个位
 *
 * @param bit
 */
static void i2c_write_bit(bool bit)
{
	SCL_OUT(0); // 拉低时钟
	I2C_DELAY();
	SDA_OUT(bit); // 输出位
	I2C_DELAY();
	SCL_OUT(1); // 拉高时钟
	I2C_DELAY();
	I2C_DELAY();
}

/**
 * @brief 读一个位
 *
 * @return true
 * @return false
 */
static bool i2c_read_bit(void)
{
	SCL_OUT(0); //
	I2C_DELAY();
	SDA_OUT(1); //
	I2C_DELAY();
	SCL_OUT(1); //
	I2C_DELAY();
	bool rusult = SDA_IN(); //
	I2C_DELAY();
	return rusult;
}

static bool i2c_write_byte(uint8_t data)
{
	for (uint8_t i = 0; i < 8; i++)
	{
		i2c_write_bit((data >> 7) & 0x01);
		data <<= 1;
	}
	return i2c_read_bit();
}

static uint8_t i2c_read_byte(bool send_ack)
{
	uint8_t result = 0;
	for (uint8_t i = 0; i < 8; i++)
	{
		result <<= 1;
		result |= (i2c_read_bit() & 0x01);
	}
	i2c_write_bit(!i2c_send_ack);

	return result;
}

static void i2c_init(void)
{
	SCL_DIR_IN();
	SDA_DIR_IN();
	SDA_OUT(1);
	SCL_OUT(1);
}

static uint32_t i2c_cmd(const uint8_t *request, uint8_t *response)
{
	uint32_t num = (2U << 16) + 2U; // 消耗2命令，回复2命令

	switch (*request++)
	{
	case I2C_CMD_START: // 发送开始条件
		i2c_start();
		*response = DAP_OK;
		break;
	case I2C_CMD_STOP: // 发送结束条件
		i2c_stop();
		*response = DAP_OK;
		break;
	case I2C_CMD_ACK: // 接收的时候是否回复ACK
		i2c_send_ack = (*request) & 0x01;
		*response = DAP_OK;
		break;
	case I2C_CMD_STATUS: // 查询总线状态
		*response++ = DAP_OK;
		*response = i2c_error_status; // 状态字
		break;
	default:
		*response = DAP_ERROR;
		break;
	}
	return num;
}

static uint32_t i2c_read(const uint8_t *request, uint8_t *response)
{
	uint32_t bytes_to_read = *request; // 数量
	*response++ = *request++;		   // 复制数量

	// 逐字节读取
	for (uint8_t i = 0; i < bytes_to_read; i++)
	{
		*response++ = i2c_read_byte(!i2c_send_ack);
	}
	return (1U << 16) + bytes_to_read + 1; // 消耗1，返回n+1
}

static uint32_t i2c_write(const uint8_t *request, uint8_t *response)
{
	uint32_t bytes_to_write = *request++; // 数量
	*response = bytes_to_write;
	uint32_t num = ((bytes_to_write + 1U) << 16) + 1U; // 消耗n+1，返回1

	// 逐字节写入
	for (uint8_t i = 0; i < bytes_to_write; i++)
	{
		if (i2c_write_byte(*request++))
		{
			i2c_error_status |= I2C_STATUS_NACK; // 读取ACK
		}
	}
	return num;
}

mult_interface_t mult_interface_i2c = {
	.mult_init = i2c_init,
	.mult_cmd = i2c_cmd,
	.mult_read = i2c_read,
	.mult_write = i2c_write,
};
